package com.geostar.constructionland.index.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.geostar.constructionland.index.entity.RateElement;
import com.geostar.constructionland.index.entity.RateExpression;
import com.geostar.constructionland.index.entity.model.RateCalModel;
import com.geostar.constructionland.index.entity.model.RateEprModel;
import com.geostar.constructionland.index.entity.model.RateModel;
import com.geostar.constructionland.index.factory.RateCalFactory;
import com.geostar.constructionland.index.mapper.RateElementMapper;
import com.geostar.constructionland.index.mapper.RateExpressMapper;
import com.geostar.constructionland.index.util.ComplexExpUtil;
import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j;
import org.nfunk.jep.JEP;
import org.nfunk.jep.Node;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Future;

/**
 * @author yell
 * @since 2020-3-16 18:18
 **/
@Service
@Slf4j
public class RateService {

    public Map getRate(RateModel rateModel){
        List<RateElement> rateElements = rateModel.getRateElements();
        HashMap<String,List<Map>> ratePoints =  new HashMap<>();
        String[] areaCodes = rateModel.getAreas();
        Map resultPoint = new HashMap();
        /**分别计算得分**/
        for (RateElement rateElement : rateElements) {
            RateCalculate rateCalculate = RateCalFactory.getRateCalByRateEle(rateElement);
            List<Map> list = rateCalculate.calRatePoint(rateElement, rateModel.getStartTime(), rateModel.getEndTime(), areaCodes);
            try{
                /**遍历list 把空值设置为0 **/
                for(Map map : list){
                    if(map.get("ELEVALUE")==null || "0".equals(map.get("ELEVALUE"))){
                        map.put("ELEVALUE",0);
                    }
                    if(!map.containsKey("AREACODE")){
                        /**默认设置44000**/
                        map.put("AREACODE","440000");
                    }
                }

                ratePoints.put(rateElement.getEleCode(),list);
            }catch (Exception e){
                log.info("rateEle:{}",rateElement);
                log.info("calSQL:{}",rateCalculate);
                e.printStackTrace();
            }
        }
        /**计算结果集**/
        for(String areaCode : areaCodes){
            JEP jep = new JEP();
            /**循环设置参数**/
            /**设置表达式**/
            try {
                for(RateElement rateIteor : rateElements){
                    Map map = findResultByAreacode(ratePoints.get(rateIteor.getEleCode()),areaCode);
                    if(map==null){
                        //全省办结并发起过补正案件总数
                        if(rateIteor.getEleCode().equals("qsbjbfqgbzdajzs")){
                            List<Map> list= ratePoints.get(rateIteor.getEleCode());
                            jep.addVariable(rateIteor.getEleCode(),list.get(0).get("ELEVALUE"));

                        }else{
                            jep.addVariable(rateIteor.getEleCode(),0);
                        }

                    }else if(map.get("AREACODE")==null){
                        jep.addVariable(rateIteor.getEleCode(),0);
                    }else{
                        jep.addVariable(rateIteor.getEleCode(),map.get("ELEVALUE"));
                    }
                }

                Node node = jep.parse(rateModel.getRateExpression().getMathExpression());
                Object evaluate = jep.evaluate(node);
                Double evalDouble = 0.0 ;
                if(evaluate instanceof BigDecimal){
                    evalDouble = ((BigDecimal) evaluate).doubleValue();
                }else{
//                    evalDouble =(Double) evaluate;
                    evalDouble = Double.valueOf(evaluate.toString());
                }

                /**如果算出来的值为 无穷大或者NAN则设置为0**/
                if(evalDouble.isInfinite() || evalDouble.isNaN()){
                    resultPoint.put(areaCode,Double.valueOf(0.0));
                }else{
                    resultPoint.put(areaCode,evaluate);
                }
            }catch (Exception e){
                resultPoint.put(areaCode,0.0);
                log.error("rateExpression:"+rateModel.getRateExpression().getMathExpression());
                e.printStackTrace();
            }
        }
        return resultPoint;
    }


    public Map findResultByAreacode(List<Map> list,String areaCode){
        Map resultMap = null;
        for(Map map:list){
            if(map!=null && map.get("AREACODE")!=null && map.get("AREACODE").equals(areaCode)){
                resultMap = map;
            }
        }
        return resultMap;
    }

    @Autowired
    RateElementMapper rateElementMapper;
    /**复杂公式计算**/
    public Map getCmpRate(RateEprModel rateEprModel) throws Exception{
        /**分别计算每个元素**/
        HashMap<String,Map> elePointMap = new HashMap<>();
        /**获取所有的表达式**/
        List<RateExpression> rEpreList = rateEprModel.getRateExpressions();
        /**------------计算所有的表达式的值--------------**/
        for(RateExpression rateExpression : rEpreList){
            /**构建评分参数RateModel**/
            RateModel rateModel = new RateModel();
            rateModel.setRateExpression(rateExpression);
            String[] rateEleCodes = rateExpression.getRateEle().split(",");
            QueryWrapper<RateElement> queryWrapper = new QueryWrapper<RateElement>();
            List<RateElement> rateElementList = rateElementMapper.selectList(queryWrapper.in("ele_code",rateEleCodes));
            /**获取公式中包含的**/
            rateModel.setRateElements(rateElementList);
            rateModel.setAreas(rateEprModel.getAreas());
            rateModel.setStartTime(rateEprModel.getStartTime());
            rateModel.setEndTime(rateEprModel.getEndTime());
            /**计算元素分数**/
            Map ratePoints = getRate(rateModel);
            elePointMap.put(rateExpression.getId(),ratePoints);
        }
        /**------------计算所有的表达式的值--------------**/
        /**按公式ID遍历**/
        Map resultMap = new HashMap();
        /**按区域遍历**/
        for(String areaCode : rateEprModel.getAreas()){
            Map areaPointMap = new HashMap();
            Map<String,Map> detailMap = new HashMap();
            for(String rateExpId : elePointMap.keySet()){
                /**获取该公式的结果集**/
                Map ratePointMap = elePointMap.get(rateExpId);
                /**从公式结果集中取出该区域结果原始值**/
                Double ratePoint_org = (Double)ratePointMap.get(areaCode);
                /**获取组合公式**/
                List<Map<String,String>> composeList = ComplexExpUtil.getComposeList(rateEprModel.getRateExpression().getMathExpression(),rateExpId);
                Map resulstMap = ComplexExpUtil.getTransedValue(rateExpId,ratePoint_org,composeList);
                String transedEle = ComplexExpUtil.getTransedEle(rateEprModel.getRateExpression().getMathExpression(),rateExpId);
                resulstMap.put("transedEle",transedEle);
                detailMap.put(transedEle,resulstMap);
            }
            /**获取计算公式**/
            String mathExpress = ComplexExpUtil.getTransedExpress(rateEprModel.getRateExpression().getMathExpression());
            /**------------聚合表达式的值--------------**/
            JEP jep = new JEP();
            /**设置公式中的参数**/
            for(String transedEle : detailMap.keySet()){
                jep.addVariable(transedEle,detailMap.get(transedEle).get("transedvalue"));
            }
            Node node = jep.parse(mathExpress);
            Object evaluate = jep.evaluate(node);
            areaPointMap.put("point",evaluate);
            areaPointMap.put("details",detailMap);
            resultMap.put(areaCode,areaPointMap);
        }
        return resultMap;
    }

    @Autowired
    RateExpressMapper rateExpressMapper;
    @Async
    public Future<Map> getCmpExps(RateCalModel rateCalModel) throws Exception{
        long startTimes =  System.currentTimeMillis();
        //格式化区域
        String[] areas=rateCalModel.getAreas().split(",");
        /**获取表达式**/
        RateExpression rateExpression = rateExpressMapper.selectById(rateCalModel.getRateExpId());
        /**获取表达式中涉及的计算元素**/
        String[] rateEleCodes =  rateExpression.getRateEle().split(",");
        /**获取元素信息**/
        QueryWrapper<RateElement> queryWrapper = new QueryWrapper<RateElement>();
        List<RateElement> rateElementList = rateElementMapper.selectList(queryWrapper.in("ele_code",rateEleCodes));
        RateModel rateModel = new RateModel();
        rateModel.setEndTime(rateCalModel.getEndTime());
        rateModel.setStartTime(rateCalModel.getStartTime());
        rateModel.setRateExpression(rateExpression);
        rateModel.setAreas(areas);
        rateModel.setRateElements(rateElementList);
        Map ratePoints = getRate(rateModel);
        long endTimes = System.currentTimeMillis();
        Map<String,Object> resultMap = new HashMap<>();
        resultMap.put("ratePoints",ratePoints);
        resultMap.put("costTime",((endTimes-startTimes)/1000));
        resultMap.put("rateExpId",rateCalModel.getRateExpId());
        return new AsyncResult(resultMap);
    }

    @Async
    public Future<Map> getScoreTask(RateElement rateElement,RateCalModel rateCalModel){
        long startTimes =  System.currentTimeMillis();
        RateCalculate rateCalculate = RateCalFactory.getRateCalByRateEle(rateElement);
        List<Map> list = rateCalculate
                .calRatePoint(rateElement, rateCalModel.getStartTime(), rateCalModel.getEndTime(), rateCalModel.getAreas().split(","));
        Map<String,Object> resu =new HashMap(4);
        resu.put("DESCRIPTION",rateElement.getDescription());
        resu.put("ID",rateElement.getId());
        resu.put("ELECODE",rateElement.getEleCode());
        resu.put("ELEVALUE",0);
        /**构建结果*/
        if(list!=null && list.size() == 1){
            resu.put("ELEVALUE",list.get(0).get("ELEVALUE"));
        }
        long endTimes = System.currentTimeMillis();
        resu.put("costTime",((endTimes-startTimes)/1000));
        return new AsyncResult<>(resu);
    }
}
